#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <functional>
#include <sys/wait.h>
#include "thread.hpp"
#include "threadpool.hpp"
#include "task.hpp"
#include "log.hpp"

// 我们要把tcp服务器设计为一个对象

// 默认端口
static const uint16_t default_port = 11111;
// 默认ip
static const std::string default_ip = "0.0.0.0";
// 默认listen 参数
static const int gbacklog = 5;

namespace zxf
{
    // class Tcp_server; // 声明一下

    // struct Thread_data
    // {
    //     Thread_data(int fd, Tcp_server *sthis) : _fd(fd), _sthis(sthis)
    //     {
    //     }
    //     int _fd;
    //     Tcp_server *_sthis;
    // };

    class Tcp_server
    {
    public:
        Tcp_server(const int16_t &port = default_port) // 构造函数
            : _port(port), _listensock(-1)
        {
        }
        // 服务器初始化
        void initServer()
        {
            // std::cout<<"Tcp_server::initServer"<<std::endl;

            // 首先创建sock (socket)
            // 函数原型
            //        #include <sys/types.h>
            //        #include <sys/socket.h>
            //        int socket(int domain, int type, int protocol);
            // domain: 协议家族
            // type: 指定socket类型
            // protocol: 指定协议 ,为 0 会自动选择协议

            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock == -1)
            {
                // std::cout << "socket error , exit" << std::endl;
                logMessage(FATAL, "%s:%d", "socket error , exit", SOCKET_ERR);
                exit(SOCKET_ERR);
            }
            // std::cout << "socket success!! " << std::endl;
            logMessage(NORMAL, "%s:%d", "socket success!! ", 0);

            // 绑定主机网络信息 (bind)
            // 函数原型
            //  #include <sys/types.h>
            //  #include <sys/socket.h>
            // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
            // sockfd: sockfd
            // addr: 要绑定的网络信息
            // addrlen: addr的长度

            struct sockaddr_in local;
            socklen_t len = sizeof(local);
            ////先初始化为0
            memset(&local, 0, len);
            // bzero(&local, len);
            // 二选其一
            local.sin_family = AF_INET;         // 协议家族
            local.sin_port = htons(_port);      // 注意主机字节序转换为网络字节序
            local.sin_addr.s_addr = INADDR_ANY; // 直接绑定任意ip即可
            // local.sin_addr.s_addr = inet_addr(default_ip.c_str());
            int ret = bind(_listensock, (const struct sockaddr *)&local, len);
            if (ret == -1)
            {
                // std::cout << "bind error , exit" << std::endl;
                logMessage(FATAL, "%s:%d", "bind error , exit", BIND_ERR);
                exit(BIND_ERR);
            }
            //std::cout << "bind success !!" << std::endl;
            logMessage(NORMAL, "%s:%d", "bind success !!", 0);

            // 设置监听状态 (listen)
            listen(_listensock, gbacklog);
            if (ret == -1)
            {
                logMessage(FATAL, "%s:%d", "listen error , exit", LISTEN_ERR);
                exit(LISTEN_ERR);
            }
            //std::cout << "listen success !!" << std::endl;
            logMessage(NORMAL, "%s:%d", "listen success !!", 0);
            // 函数原型
            //  #include <sys/types.h>
            //  #include <sys/socket.h>
            //  int listen(int sockfd, int backlog);
            // sockfd: 将要设置监听状态的 sockfd,
            // backlog: 以后讲解
        }

        // static void func(int sig)
        // {
        //     std::cout << "pid: " << getpid() << "; " << sig << "号信号被捕捉！！" << std::endl;
        //     // 假设这个父进程有很多的子进程，有多个子进程同时退出，会给父进程发送多次14号信号
        //     // 但是信号只能保存一个，handler函数只能执行一次。
        //     // 所有这里需要循环式的等待，直至当前所有的子进程都被成功等待，才结束。
        //     // waitpid，使用WNOHANG参数后，为非阻塞等待，
        //     // 等待成功返回被回收子进程的pid。
        //     // 等待失败（所有子进程都没成功回收）返回0；
        //     // 错误返回-1；
        //     pid_t id;
        //     while (id = waitpid(-1, nullptr, WNOHANG) > 0)
        //     {
        //         std::cout << "等待成功！！" << std::endl;
        //     }
        //     // 此时所有退出的子进程都被成功等待。
        // }
        // 开始运行服务器
        void start()
        {
            // std::cout<<"Tcp_server::start"<<std::endl;
            //  获取一个新的链接,开启通讯 (accept)
            // #include <sys/types.h>
            // #include <sys/socket.h>
            // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

            // 捕捉SIGCHLD信号
            // signal(SIGCHLD, func); // 一般在开头调用

            // 忽略SIGCHLD信号
            // signal(SIGCHLD, SIG_IGN); //一般在开头调用

            // 线程池子
            // 创建线程池并且初始化
            ThreadPool<Task> *pool = new ThreadPool<Task>();
            // 初始化
            pool->start();

            for (;;)
            {
                struct sockaddr_in net;
                socklen_t len = sizeof(net);
                bzero(&net, len);
                int accept_fd = accept(_listensock, (struct sockaddr *)&net, &len);
                // 没有获取到新连接 会阻塞在这里
                // 知道新的链接被获取到. accept 返回

                if (accept_fd == -1)
                {
                    //std::cout << "accept error , exit" << std::endl;
                    logMessage(ERROR, "%s", "listen error , exit");
                    continue;
                }
                //std::cout << "accept success !! " << std::endl;
                logMessage(NORMAL, "%s:%d", "listen error , exit", 0);

                // 处理链接
                // 这里可以使用 多进程,多线程,线程池,等等处理方式

                // bady版
                //  先来个单进程版的 (baby版本)//只能允许一个链接 //只能串行的执行代码
                // hander(accept_fd);

                //////////////////////////////////////////////// 多进程版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 多进程版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 多进程版本的////////////////////////////////////////////////////////////////////////////////
                // int id = fork();
                // if (id == 0)
                // {
                //     // // 子进程
                //     // if (fork() > 0)
                //     // {
                //     //     exit(0);
                //     // }
                //     // 孙子进程 --->孤儿进程
                //     hander(accept_fd);
                //     // 我们让孙子进程去执行对应的程序我们不管进程返回信息
                //     exit(0);
                // }

                // // 父进程 需要等待进程
                // // 有两种方式 1.创建孙子进程 2.信号
                // // 1.创建孙子进程
                // // int ret = waitpid(id, nullptr, 0); // 不关心进程退出信息
                // // // waitpid如果成功返回等待子进程的ID，失败返回-1
                // // if (ret < 0)
                // // {
                // //     std::cout << "waitpid error , exit" << std::endl;
                // //     exit(WAAITPID_ERR);
                // // }
                // // std::cout << "waitpid success !!" << std::endl;

                // // 2.信号SIGCHLD(17号信号)
                // // 可以直接捕捉SIGCHLD信号 ,也可以忽略SIGCHLD信号
                // // 捕捉SIGCHLD信号
                // // signal(SIGCHLD, func); //一般在开头调用

                // // 忽略SIGCHLD信号
                // // signal(SIGCHLD, SIG_IGN); //一般在开头调用
                //////////////////////////////////////////////// 多线程版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 多线程版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 多线程版本的////////////////////////////////////////////////////////////////////////////////

                // // 函数原型
                // //  #include <pthread.h>
                // //  int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

                // pthread_t tid;
                // Thread_data* args = new Thread_data(accept_fd, this);
                // pthread_create(&tid, NULL, func, (void*)args);

                // //pthread_join(tid, nullptr); //使用这个还是串行的执行 ,主线程 会阻塞在这里 没有获取新的链接
                // //所以不推荐以上方法

                // //所以要用到线程分离,主线程分离或者从线程自己分离自己

                //////////////////////////////////////////////// 线程池版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 线程池版本的////////////////////////////////////////////////////////////////////////////////
                //////////////////////////////////////////////// 线程池版本的////////////////////////////////////////////////////////////////////////////////

                // 创建Task ,push即可
                Task *t = new Task(accept_fd);

                pool->push(*t);
            }

            // 以上代码 应该在 一个循环内 ,也就是说,TCP协议 ,可以获取很多链接
            // 循环的accept.获取链接
        }

        // static void *func(void *args)
        // {
        //     // 先分离自己
        //     pthread_detach(pthread_self());

        //     // 执行逻辑代码
        //     Thread_data *data = static_cast<Thread_data *>(args);
        //     data->_sthis->hander(data->_fd);
        //     return nullptr;
        // }

    private:
        // ip ,一般使用 INADDR_ANY / 0.0.0.0 //也就是表示本机的所有IP
        // 端口
        int16_t _port;   // 服务器绑定的port
        int _listensock; // 监听套接字
    };

} // namespace zxf  end!!



